package com.lenovo.srl.dil.tiltbase.lib.utils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import android.content.Context;
import android.os.Environment;
import android.util.Log;

import com.google.code.microlog4android.Level;
import com.google.code.microlog4android.appender.AbstractAppender;

public class FileAppender extends AbstractAppender {
	public static final long MAX_LOG_SIZE = 1 << 22;
	private static final String TAG = "Microlog.FileAppender";

	public static final String DEFAULT_FILENAME = "microlog.txt";

	private String fileName = DEFAULT_FILENAME;

	private PrintWriter writer;

	private boolean append = true;

	private File mSdCardLogFile = null;

	Context mContext = null;

	/**
	 * Create a file appender using the specified application context. Note:
	 * your application must hold android.permission.WRITE_EXTERNAL_STORAGE to
	 * be able to access the SDCard.
	 * 
	 * @param c
	 */
	public FileAppender(Context c) {
		mContext = c;
	}

	/**
	 * Create a file appender without application context. The logging file will
	 * be placed in the root folder and will not be removed when your
	 * application is removed. Use FileAppender(Context) to create a log that is
	 * automatically removed when your application is removed Note: your
	 * application must hold android.permission.WRITE_EXTERNAL_STORAGE to be
	 * able to access the SDCard.
	 */
	public FileAppender() {
	}

	/**
	 * @see com.google.code.microlog4android.appender.AbstractAppender#open()
	 */
	@Override
	public synchronized void open() throws IOException {
		File logFile = getLogFile();
		logOpen = false;

		if (logFile != null) {
			if (!logFile.exists()) {
				if (!logFile.createNewFile()) {
					Log.e(TAG, "Unable to create new log file");

				}
			}

			FileOutputStream fileOutputStream = new FileOutputStream(logFile,
					append);

			if (fileOutputStream != null) {
				writer = new PrintWriter(fileOutputStream);
				logOpen = true;
			}
		}
	}

	/**
	 * @see com.google.code.microlog4android.appender.AbstractAppender#clear()
	 */
	@Override
	public void clear() {
		throw new UnsupportedOperationException();
	}

	/**
	 * @see com.google.code.microlog4android.appender.AbstractAppender#close()
	 */
	@Override
	public synchronized void close() throws IOException {
		Log.i(TAG, "Closing the FileAppender");
		if (writer != null) {
			writer.close();
		}
	}

	public synchronized void writeLog(String clientID, String name, long time,
			Level level, Object message, Throwable throwable) {
		if (logOpen && formatter != null && writer != null) {
			writer.println(formatter.format(clientID, name, time, level,
					message, throwable));
			writer.flush();

			if (throwable != null) {
				throwable.printStackTrace();
			}
		} else if (formatter == null) {
			Log.e(TAG, "Please set a formatter.");
		}

	}

	/**
	 * Set the filename to be used
	 * 
	 * @param fileName
	 *            the filename to log to
	 */
	public void setFileName(String fileName) {
		// TODO Throw IllegalArgumentException if the filename is null.
		if (fileName != null) {
			this.fileName = fileName;
		}
	}

	/**
	 * Set if we shall append the file when logging or if we shall start over
	 * all again when starting a new session.
	 * 
	 * @param append
	 *            the append to set (default = false)
	 */
	public void setAppend(boolean append) {
		this.append = append;
	}

	/**
	 * Android 1.6-2.1 used {@link Environment#getExternalStorageDirectory()} to
	 * return the (root) external storage directory. Folders in this subdir were
	 * shared by all applications and were not removed when the application was
	 * deleted. Starting with andriod 2.2, Context.getExternalFilesDir() is
	 * available. This is an external directory available to the application
	 * which is removed when the application is removed.
	 * 
	 * This implementation uses Context.getExternalFilesDir() if available, if
	 * not available uses {@link Environment#getExternalStorageDirectory()}.
	 * 
	 * @return a File object representing the external storage directory used by
	 *         this device or null if the subdir could not be created or proven
	 *         to exist
	 */
	protected synchronized File getExternalStorageDirectory() {
		File externalStorageDirectory = new File(Environment
				.getExternalStorageDirectory().getAbsolutePath() + "/TiltBase");
		if (externalStorageDirectory != null) {
			if (!externalStorageDirectory.exists()) {
				if (!externalStorageDirectory.mkdirs()) {
					externalStorageDirectory = null;
					Log.e(TAG, "mkdirs failed on externalStorageDirectory "
							+ externalStorageDirectory);
				}
			}
		}
		return externalStorageDirectory;
	}

	/**
	 * @return the log file used to log to external storage
	 */
	public synchronized File getLogFile() {

		if (mSdCardLogFile == null) {
			String externalStorageState = Environment.getExternalStorageState();
			if (externalStorageState.equals(Environment.MEDIA_MOUNTED)) {
				File externalStorageDirectory = getExternalStorageDirectory();

				if (externalStorageDirectory != null) {
					mSdCardLogFile = new File(externalStorageDirectory,
							fileName);
				}
			}

			if (mSdCardLogFile == null) {
				Log.e(TAG, "Unable to open log file from external storage");
			}
		}

		return mSdCardLogFile;
	}

	@Override
	public long getLogSize() {
		File file = getLogFile();
		if (file != null && file.exists()) {
			return file.length();
		}
		return -1;
	}

	@Override
	public synchronized void doLog(String clientID, String name, long time,
			Level level, Object message, Throwable throwable) {
		long curLogSize = getLogSize();
		if (curLogSize >= MAX_LOG_SIZE) {
			try {
				close();
				File file = getLogFile();
				DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
				String now = df.format(new Date());
				if (file != null && file.exists()) {
					file.renameTo(new File(file.getAbsolutePath() + "." + now));
				}
				open();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		writeLog(clientID, name, time, level, message, throwable);
	}
}
